using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;
using System.Threading;

namespace FEZThinkSpeak
{
    /// <summary>
    /// Class to read the single wire Humitidy/Temperature DHT11 sensor
    /// It uses 2 interrupt pins connected together to access the sensor quick enough
    /// </summary>
    public class DHT11 : IDisposable
    {
        /// <summary>
        /// Temperature in celcius degres
        /// </summary>
        public float Temperature { get; private set; }

        /// <summary>
        /// Humidity in percents
        /// </summary>
        public float Humidity { get; private set; }

        /// <summary>
        /// Temperature difference
        /// </summary>
        public float TemperatureDifference { get; private set; }


        /// <summary>
        /// Temperature difference
        /// </summary>
        public float HumidityDifference { get; private set; }

        /// <summary>
        /// If not empty, gives the last error that occured
        /// </summary>
        public string LastError { get; private set; }

        private TristatePort _dht11out;
        private PinCapture _dht11in;

        private float lastMeasuredTemp = 0;
        private float lastMeasuredHum = 0;
        /// <summary>
        /// Constructor. Needs to interrupt pins to be provided and linked together in Hardware.
        /// Blocking call for 1s to give sensor time to initialize.
        /// </summary>
        public DHT11(Cpu.Pin In, Cpu.Pin Out)
        {
            _dht11out = new TristatePort(Out, false, false, Port.ResistorMode.PullUp);
            _dht11in = new PinCapture(In, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
            if (_dht11out.Active == false) _dht11out.Active = true; // Make tristateport "output" 
            _dht11out.Write(true);   //"high up" (standby state)
            Thread.Sleep(1000); // 1s to pass the "unstable status" as per the documentation
        }

        #region IDisposable Members

        public void Dispose()
        {
            _dht11out.Dispose();
            _dht11in.Dispose();
        }

        #endregion

        /// <summary>
        /// Access the sensor. Returns true if successful, false if it fails.
        /// If false, please check the LastError value for reason.
        /// </summary>
        public bool ReadSensor()
        {
            uint[] buffer = new uint[90];
            int nb, i;

            // Testing if the 2 pins are connected together
            bool rt = _dht11in.InternalPort.Read();  // Should be true
            _dht11out.Write(false);  // "low down" : initiate transmission
            bool rf = _dht11in.InternalPort.Read();  // Should be false
            if (!rt || rf)
            {
                LastError = "The 2 pins are not hardwired together !";
                _dht11out.Write(true);   //"high up" (standby state)
                return false;
            }
            Thread.Sleep(20);       // For "at least 18ms" as per the documentation
            _dht11out.Write(true);   //"high up" then listen
            nb = _dht11in.Read(false, buffer, 0, 90, 10);  // get the sensor answer
            if (nb < 81)
            {
                LastError = "Did not receive enough data from the sensor";
                return false;
            }
            nb -= 2; // skip last 50us down          
            byte checksum = 0, decimalT = 0, integralT = 0, decimalRH = 0, integralRH = 0;
            for (i = 0; i < 8; i++, nb -= 2) checksum |= (byte)(buffer[nb] > 50 ? 1 << i : 0);
            for (i = 0; i < 8; i++, nb -= 2) decimalT |= (byte)(buffer[nb] > 50 ? 1 << i : 0);
            for (i = 0; i < 8; i++, nb -= 2) integralT |= (byte)(buffer[nb] > 50 ? 1 << i : 0);
            for (i = 0; i < 8; i++, nb -= 2) decimalRH |= (byte)(buffer[nb] > 50 ? 1 << i : 0);
            for (i = 0; i < 8; i++, nb -= 2) integralRH |= (byte)(buffer[nb] > 50 ? 1 << i : 0);
            if (((integralRH + decimalRH + integralT + decimalT) & 0xFF) != checksum)
            {
                LastError = "Checksum Error";
                return false;
            }
            Temperature = ((float)integralT) + ((float)decimalT) / 10;
            Humidity = ((float)integralRH) + ((float)decimalRH) / 10;

            if (lastMeasuredTemp == 0)
            {
                lastMeasuredTemp = Temperature;
            }
            else
            {
                TemperatureDifference = (Temperature - lastMeasuredTemp);
            }

            if (lastMeasuredHum == 0)
            {
                lastMeasuredHum = Humidity;
            }
            else
            {
                HumidityDifference = (Humidity - lastMeasuredHum);
            }
            LastError = "";
            return true;
        }
    }
}
